package civilian;

import jade.core.AID;
import jade.core.Agent;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;
import jade.proto.ContractNetResponder;

import java.util.List;
import java.util.Random;

import moves.Movement.Direction;
import moves.MovementSender;
import path.DistanceList;
import path.Path;
import path.PathTest;
import agents.UtilsAgents;
import building.Cell;
import building.InfoAgent;
import building.InfoGame;

public class ProtocolContractNetResponder {
	private Path short_path;
	private int my_x, my_y;
	private int my_f;
	private MovementSender ms;
	private boolean freeAgent = true;// true = free false= transport
	private InfoAgent infoAgent;
	private Cell goDescarga;
	Cell content = null;
	SendFinishLoad protocolSendFinishLoad;
	SendFinishDownload protocolSendFinishDownload;
	ReceiveMovesPetitions protocolReceiveMove;
	private boolean accepted;
	private boolean existGarbatge = false;
	private boolean existAgentGarbatge = false;
	private Agent myAgent;
	Direction step = null;
	AID sender;
	private SendMovePetition protocolSendMove;

	/**
	 * Control for each turn
	 * 
	 * @param infoGame
	 *            the infoGame to set
	 */

	@SuppressWarnings("static-access")
	public void setInfoGame(InfoGame infoGame, AID sender) {
		this.infoGame = infoGame;
		this.sender = sender;
		accepted = false;
		existGarbatge = false;
		existAgentGarbatge = false;
		int distance = -1;
		infoAgent = agents.UtilsAgents.findAgent(myAgent.getAID(), infoGame)
				.getAgent();
		Cell varAgent = agents.UtilsAgents.findAgent(this.myAgent.getAID(),
				infoGame);
		// System.out.println("Agent " + infoAgent.getAID()+ "in position" +
		// varAgent);
		Cell begin = new Cell(Cell.FREE);

		// control if harvester is transport the garbage

		my_x = agents.UtilsAgents.findAgent(this.myAgent.getAID(), infoGame).getColumn();
		my_y = agents.UtilsAgents.findAgent(this.myAgent.getAID(), infoGame).getRow();
		my_f = agents.UtilsAgents.findAgent(this.myAgent.getAID(), infoGame).getFloor();
		Cell door = new Cell(Cell.DOOR);

		if (content != null && content.getFloor() == my_f) {
			if (agents.UtilsAgents.imBlocked(infoGame, this.myAgent)) {
				System.out.println("I am blocked" + this.myAgent.getAID());
				List<Cell> receivers = UtilsAgents.getNeighboors(
						myAgent.getAID(), infoGame);
				System.out.println("MIS VECINOS" + receivers.size());
				if (receivers.size() >= 1) {
					AID blockingAgent = receivers.get(0).getAgent().getAID();
					System.out.println("im sending an unblock message");
					// protocolSendMove.addBehaviour(myAgent, blockingAgent);

				}
			}

			try {
				door = infoGame.getCell(content.getRow(), content.getColumn(),
						content.getFloor());

				distance = evaluateAction(door);
				// System.out.println("current position"+my_f+ "x"+my_x+ "y"
				// +my_y);
				begin.setColumn(my_x);
				begin.setRow(my_y);
				begin.setFloor(my_f);
				System.out.println("distancia calculada por el civilian"
						+ this.myAgent.getLocalName() + ":" + distance
						+ "in floor" + my_f);
			} catch (Exception e) {

				System.out.println("setinfo" + e.toString());
			}

			// look at the perimeter
			if (distance == 0) {

				if (my_f == 0) {
					// notifyFinishedLoad(infoGame);
					protocolSendFinishDownload.addBehaviour(myAgent, sender);
					step = step.OUT;
					System.out.println("estoy a salvo" + myAgent.getLocalName()
							+ " " + sender);
					ms.setDest(sender);
					ms.out(step);
					freeAgent = true;
					accepted = false;
					notifyFinishedLoad(infoGame);// protocolSendFinishLoad
					return;
				} else {
					Cell stairs = door;
					stairs.setRow(infoGame.getNrows() - 1);
					stairs.setFloor(content.getFloor() - 1);
					System.out.println("is there an agent in the stairs?"
							+ stairs + " " + stairs.isThereAnAgent());
					if (stairs.isThereAnAgent()) {
						System.out.println("hay un agente en las escaleras"
								+ door + door.getAgent());

					}

					freeAgent = true;
					accepted = true;
					step = step.UPFLOOR;
					System.out.println("paso al siguiente piano"
							+ this.myAgent.getLocalName());
					ms.setDest(sender);
					ms.go_floor(step);
					notifyFinishedLoad(infoGame);

					return;

				}
			}

			else if (distance == 1) {

				try {
					if (door.isThereAnAgent()) {
						ms.go(Direction.NOP);
						System.out
								.println("Civilian:hay un agente en la puerta"
										+ door + door.getAgent());
					} else {
						// protocolSendFinishDownload.addBehaviour(myAgent);
						System.out.println(myAgent.getLocalName()
								+ "la puerta es libre");
						// freeAgent= true;
						// accepted=false;
						// step = getNextStep();
						// System.out.println(myAgent.getLocalName() + "step11"
						// + getNextStepDesti(content));
						ms.setDest(sender);
						step = getNextStep();
						ms.go(step);
						return;

					}
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}// if
			else if (distance > 1 && distance != 10000) {
				distance = evaluateAction(door);
				System.out.println("estoy a mas de un paso de la puerta"
						+ myAgent.getLocalName() + "destination cell" + door);
				step = getNextStep();
				System.out.println("step: " + step);
				ms.setDest(sender);
				ms.go(step);
				return;
			} else {
				System.out
						.println("Civilian:I havent found a path to the exit "
								+ this.myAgent.getLocalName());
				List<Cell> receivers = UtilsAgents.getNeighboors(
						myAgent.getAID(), infoGame);
				System.out.println("MIS VECINOS" + receivers.size());
				if (receivers.size() > 0) {
					ms.setDest(sender);
					ms.go(getNextStepDesti(receivers.get(0)));
				}

				return;

			} // else

		}

	}

	public void moveRandomly(InfoGame infoGame, Cell c) {
		Random rand = new Random();

		List<Cell> myNeigh = null;
		myNeigh = agents.UtilsAgents.getNeighboors(c.getAgent().getAID(),
				infoGame);

		int randstep = rand.nextInt(3);
		System.out.println("rand step" + randstep + myNeigh);
		switch (randstep) {

		case 1:
			step = step.RIGHT;
			break;
		case 2:
			step = step.LEFT;
			break;
		case 3:
			step = step.DOWN;
			break;
		case 0:
			step = step.UP;
			break;
		}

		System.out.println("move randomly" + step + " "
				+ this.myAgent.getLocalName());

		// ms.go(step);

	}

	// To indicate that the civilian arrived at the door
	private void notifyFinishedLoad(InfoGame infoGame) {
		DistanceList list = new DistanceList();

		Cell currCell = agents.UtilsAgents
				.findAgent(myAgent.getAID(), infoGame);// my pos
		System.out.println("notify finish load" + myAgent.getAID()
				+ " in piano" + currCell.getFloor());

		try {
			goDescarga = protocolSendFinishLoad.blockingMessage(myAgent,
					currCell);
			System.out.println(this.myAgent.getLocalName() + "Arrived at door");// +
																				// goDescarga.getRow()+
																				// goDescarga.getColumn()
																				// +goDescarga.getFloor());
		} catch (Exception e) {
			System.out.println("Exception" + this.myAgent.getLocalName()
					+ "non ha trovato la porta" + e.toString());

		}

		freeAgent = false;
	}

	private InfoGame infoGame;

	/**
	 * Receive a cell where content the material and the position where manager
	 * harvester want to go the harvester.
	 * 
	 * @param Agent
	 * @param Cell
	 */
	public void addBehaviour(Agent agent) {
		myAgent = agent;
		MessageTemplate mt1 = MessageTemplate
				.MatchProtocol(agents.UtilsAgents.CONTRACT_NET);
		MessageTemplate mt2 = MessageTemplate.MatchPerformative(ACLMessage.CFP);
		agent.addBehaviour(new ProtocolContractNetRes(agent, MessageTemplate
				.and(mt1, mt2)));
	}

	public void addBehaviour(Agent agent, Cell content) {
		myAgent = agent;
		MessageTemplate mt1 = MessageTemplate
				.MatchProtocol(agents.UtilsAgents.CONTRACT_NET);
		MessageTemplate mt2 = MessageTemplate.MatchPerformative(ACLMessage.CFP);

		agent.addBehaviour(new ProtocolContractNetRes(agent, MessageTemplate
				.and(mt1, mt2)));
	}

	public class ProtocolContractNetRes extends ContractNetResponder {
		/**
		 * ProtocolContractNet
		 */
		private static final long serialVersionUID = 1L;

		public ProtocolContractNetRes(Agent myAgent, MessageTemplate mt) {
			super(myAgent, mt);
			// System.out.println("Civilian: into cnet constructor");
			ms = new MovementSender(myAgent, myAgent.getAID(),
					agents.UtilsAgents.EVACUATOR_AGENT);
			protocolSendFinishLoad = new SendFinishLoad();
			protocolSendFinishDownload = new SendFinishDownload();
			protocolSendMove = new SendMovePetition();
			protocolReceiveMove = new ReceiveMovesPetitions();
			protocolReceiveMove.addBehaviour(this.myAgent);

		}

		/**
		 * Execute when receive a CFP message and need return integer with
		 * distance and (not-understood, refuse o propose).
		 */
		protected ACLMessage prepareResponse(ACLMessage msg) {
			int distance=-1;
			try {
				content = (Cell) msg.getContentObject();
				System.out.println(this.myAgent.getLocalName()
						+ "\n content recibido" + content + msg.getSender());

			} catch (UnreadableException e) {
				System.out.println("responding" + this.myAgent.getLocalName());
				e.printStackTrace();
			}
			ACLMessage reply = msg.createReply();
			// Or refuse or not-understood.

			if (infoAgent.getPanic() >= 80) {
				reply.setPerformative(ACLMessage.REFUSE);
				// reply.setContent("In panic");
				System.out.println("Im in panic" + this.myAgent.getLocalName());
				return reply;
			}

			/*
			 * if(infoAgent.imBlocked(this.myAgent.getAID(), infoGame) ) {
			 * reply.setPerformative(ACLMessage.REFUSE); //
			 * reply.setContent("In panic");
			 * System.out.println("Info agent refuse because is blocked" +
			 * this.myAgent.getLocalName()); return reply; }
			 */

			// if already accepted a contract, refuse the new one
			if (accepted || infoAgent.getInjured() || infoAgent.died) {
				reply.setPerformative(ACLMessage.REFUSE);
				System.out.println("already accepted"
						+ this.myAgent.getLocalName());
				return reply;
			}

			try {
				System.out.println("!content from civ"
						+ this.myAgent.getLocalName() + "to evacuator"
						+ content);
				distance = evaluateAction(content);
				System.out.println("distance proposed" + distance);
				if (distance == 10000) {
					reply.setPerformative(ACLMessage.REFUSE);
					//reply.setContent("1");
					System.out.println("refuse because of distance");
					return reply;
				}

				else {
					reply.setPerformative(ACLMessage.PROPOSE);
					reply.setContent(Integer.toString(distance));
					System.out.println("propose");

					return reply;
					// }
				}
			} catch (Exception e) {
				System.out.println("NO SE HA CALCULADO LA DISTANCIA" + distance
						+ e.toString());

				// }
			}
			// }
			reply.setPerformative(ACLMessage.REFUSE);
			return reply;

		}

		/**
		 * Execute when receive Accept-proposal. The Parameters are CFP initial
		 * and the response (Propose) Return Inform or Failure.
		 */
		protected ACLMessage prepareResultNotification(ACLMessage cfp,
				ACLMessage propose, ACLMessage accept) {
			accepted = true;
			freeAgent = true;
			ACLMessage inform = accept.createReply();
			// Your code.
			inform.setPerformative(ACLMessage.CONFIRM);

			Cell begin = new Cell(Cell.FREE);

			// Search my position
			int myx = agents.UtilsAgents.findAgent(this.myAgent.getAID(),
					infoGame).getColumn();
			int myy = agents.UtilsAgents.findAgent(this.myAgent.getAID(),
					infoGame).getRow();
			int myf = agents.UtilsAgents.findAgent(this.myAgent.getAID(),
					infoGame).getFloor();

			begin.setColumn(myx);
			begin.setRow(myy);
			begin.setFloor(myf);
			System.out.println(this.myAgent.getLocalName()
					+ "cella del agente: " + begin.getRow() + " "
					+ begin.getColumn() + "floor" + begin.getFloor());
			System.out.println(this.myAgent.getLocalName()
					+ "cella del DESTINO" + content.getRow() + " "
					+ content.getColumn() + "floor" + content.getFloor());

			System.out.println(this.myAgent.getLocalName() + "distance "
					+ agents.UtilsAgents.cellDistance(begin, content));
			// look at the perimeter
			if (agents.UtilsAgents.cellDistance(begin, content) == 1) {

				try {
					/*
					 * if(content.getGarbageUnits()>0){
					 * ms.get(getNextStepDesti(content
					 * ),moves.Movement.typeFromInt
					 * (infoAgent.getCurrentType())); }
					 */
					ms.setDest(sender);
					ms.go(getNextStepDesti(content));
					System.out.println("estoy a un paso y notifico"
							+ this.myAgent.getLocalName());
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}else if (agents.UtilsAgents.cellDistance(begin, content) == 10000) {

				try {
					
					ms.setDest(sender);
					ms.go(getNextStepDesti(content));
					System.out.println("estoy a un paso y notifico"
							+ this.myAgent.getLocalName());
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}

			}  
			else {
				System.out.println("mi sposto" + this.myAgent.getLocalName());
				ms.setDest(sender);
				ms.go(getNextStepDesti(content));
			}

			return inform;
		}

		/**
		 * Execute when the message is Reject-proposal.
		 */
		protected void handleRejectProposal(ACLMessage cfp, ACLMessage propose,
				ACLMessage reject) {
			System.out.println("Errore" + this.myAgent.getLocalName());
		}
	}

	/**
	 * 
	 * @return the next Direction where harvester have go, this Direction is
	 *         calculated in short Path
	 */
	private Direction getNextStep() {
		try {
			int destination_x = short_path.getX(1);
			int destination_y = short_path.getY(1);

			if (infoAgent.injured || infoAgent.getPanic() == 80
					|| infoAgent.died) {
				System.out.println(infoAgent.getAID()
						+ "not moved because is in panic");
				return Direction.NOP;
			}

			if (my_x < destination_x && my_y == destination_y) {
				System.out.println("next action right");
				return Direction.RIGHT;

			}
			if (my_x > destination_x && my_y == destination_y) {
				System.out.println("next action left");
				return Direction.LEFT;
			}
			if (my_y < destination_y && my_x == destination_x) {
				System.out.println("next action down");
				return Direction.DOWN;
			}
			if (my_y > destination_y && my_x == destination_x) {
				System.out.println("next action up");
				return Direction.UP;
			}
			if (my_y > destination_y && my_x > destination_x) {
				System.out.println("next action upleft");
				return Direction.UPLEFT;
			}
			if (my_y < destination_y && my_x < destination_x) {
				System.out.println("next action down right");
				return Direction.DOWNRIGHT;
			}

			if (my_x < destination_x && my_y > destination_y) {
				System.out.println("next action upright");
				return Direction.UPRIGHT;
			}
			if (my_x > destination_x && my_y < destination_y) {
				System.out.println("next action downleft");
				return Direction.DOWNLEFT;
			}
		} catch (Exception e) {
			System.out.println("exception in get next step");
			e.printStackTrace();
		}
		return Direction.NOP;

	}

	/**
	 * 
	 * @param dest
	 * @return the next Direction where harvester have go
	 */
	private Direction getNextStepDesti(Cell dest) {
		int destination_x = dest.getColumn();
		int destination_y = dest.getRow();

		if (infoAgent.injured || infoAgent.getPanic() == 80 || infoAgent.died) {
			System.out.println(infoAgent.getAID()
					+ "not moved because is in panic");
			return Direction.NOP;

		}

		if (my_x < destination_x && my_y == destination_y) {
			System.out.println("next action right");
			return Direction.RIGHT;

		}
		if (my_x > destination_x && my_y == destination_y) {
			System.out.println("next action left");
			return Direction.LEFT;
		}
		if (my_y < destination_y && my_x == destination_x) {
			System.out.println("next action down");
			return Direction.DOWN;
		}
		if (my_y > destination_y && my_x == destination_x) {
			System.out.println("next action up");
			return Direction.UP;
		}
		if (my_y > destination_y && my_x > destination_x) {
			return Direction.UPLEFT;
		}
		if (my_y < destination_y && my_x < destination_x) {
			return Direction.DOWNRIGHT;
		}

		if (my_x < destination_x && my_y > destination_y) {
			return Direction.UPRIGHT;
		}
		if (my_x > destination_x && my_y < destination_y) {
			return Direction.DOWNLEFT;
		}
		System.out.println("next action nop");
		return Direction.NOP;

	}

	/**
	 * 
	 * @param cell
	 * @return the best distance for harvester to go a one cell
	 */
	private int evaluateAction(Cell cell) {

		int xfinal = cell.getColumn();
		int yfinal = cell.getRow();
		int zfinal = cell.getFloor();
		// return the short path
		// Search my position
		my_x = agents.UtilsAgents.findAgent(this.myAgent.getAID(), infoGame)
				.getColumn();
		my_y = agents.UtilsAgents.findAgent(this.myAgent.getAID(), infoGame)
				.getRow();
		my_f = agents.UtilsAgents.findAgent(this.myAgent.getAID(), infoGame)
				.getFloor();

		int distFinal = 10000;
		PathTest test = null;
		if (zfinal == my_f) {
			test = new PathTest(infoGame);

			System.out.println("*****Protocol Civi evaluate action" + zfinal);
			System.out.println(this.myAgent.getAID() + "Im in floor" + my_f
					+ "destination" + zfinal);
			System.out.println("inicial agent " + this.myAgent.getLocalName()
					+ "in pos" + my_x + " -" + my_y + " in floor" + my_f);
			System.out.println("final   agent " + this.myAgent.getLocalName()
					+ "in pos" + xfinal + " -" + yfinal + " in floor" + zfinal);

			if (my_y == yfinal && xfinal == my_x && zfinal == my_f) {

				return 0;

			}

			// option 1
			test.PosicioInicial(my_x, my_y, 1, my_f);
			// System.out.println("calcolo pos inicial");
			Path stepsPathFinal1 = test.PosicioFinal(xfinal, yfinal, 1, zfinal);
			if(stepsPathFinal1 != null)
				System.out.println(" calcolo path1 " + stepsPathFinal1.getLength());
			else 
				System.out.println( "null1");
			// option 2
			test.PosicioInicial(my_x, my_y, 2, my_f);
			Path stepsPathFinal2 = test.PosicioFinal2(xfinal, yfinal, 2, zfinal);
			
			if(stepsPathFinal2 != null)
				System.out.println(" calcolo path2 " + stepsPathFinal2.getLength());
			else 
				System.out.println( "null2");
			// System.out.println("agent " + this.myAgent.getLocalName() +
			// "in pos2"+my_x + " -"+my_y);

			// Control if possible the second option
		/*	if (stepsPathFinal2 != null) {
				distFinal = test.distanciaPesos(stepsPathFinal2);
				short_path = stepsPathFinal2;
				if (stepsPathFinal1 != null) {

					int distPesosOp1 = test.distanciaPesos(stepsPathFinal1);
					if (distFinal > distPesosOp1) {
						distFinal = distPesosOp1;
						short_path = stepsPathFinal1;
						// stepsPathFinal1.printPath();
					}
				}

			}*/
			
			if (stepsPathFinal1 != null) {

				int distPesosOp1 = test.distanciaPesos(stepsPathFinal1);
				if (distFinal > distPesosOp1) {
					distFinal = distPesosOp1;
					short_path = stepsPathFinal1;
				    stepsPathFinal1.printPath();
				}
			}
			else{
				distFinal = test.distanciaPesos(stepsPathFinal2);
				short_path = stepsPathFinal2;
				 stepsPathFinal2.printPath();
				
				
			}
			
			return distFinal;

		} else
			return distFinal;
	}

}
